﻿// ********************************
// 2010.10.16
// SNMP设备发现类
// Author:卢融凯
// ********************************
using System;
using System.Collections.Generic;
using System.Net;
using Lextm.SharpSnmpLib;
using Lextm.SharpSnmpLib.Messaging;
using SManage.Model;
using System.Collections;
using System.Threading;
using System.Linq;
using System.Threading.Tasks;
using System.Collections.Concurrent;

namespace SManage.TraceEngine
{
    public class DeviceDiscoverer
    {
        private IList<IPAddress> _excludeIPList = new List<IPAddress>();
        //private IList<IPAddress> _toDiscoveryList = new List<IPAddress>();
        private ConcurrentQueue<IPAddress> _toDiscoveryQueue = new ConcurrentQueue<IPAddress>();
        private Object excludeLocker = new Object();
        //private Object discoveryLocker = new Object();
        private Discoverer _discoverer;
        private VersionCode _version;
        public event EventHandler<FoundSyncItemEventArgs> FoundSyncItem;
        public event EventHandler<DiscoverFinishedEventArgs> DiscoverFinished;

        public DeviceDiscoverer():this(VersionCode.V2) { }

        public DeviceDiscoverer(VersionCode version) {
            _discoverer = new Discoverer();
            _discoverer.AgentFound += AgentFound;
            _version = version;
        }

        private void AgentFound(Object sender, AgentFoundEventArgs e) {
            var ip = e.Agent.Address;

            var arg = new FoundSyncItemEventArgs(new SyncItem(ip, _version));
            OnFoundSyncItem(arg);

            var interfaceList = DiscovererUtil.GetInterfaceIps(ip);
            var neighbors = DiscovererUtil.GetNeighborIps(ip);  

            foreach (var i in neighbors) {
                if (_excludeIPList.Contains(i)) {
                    continue;
                }
                _toDiscoveryQueue.Enqueue(i);
            }          
            Monitor.Enter(excludeLocker);
            _excludeIPList = _excludeIPList
                                .Concat(interfaceList)
                                .Concat(neighbors)
                                .Distinct().ToList();
            Monitor.Exit(excludeLocker);

            DiscoveryOther();
        }

        private void DiscoveryOther() {
            IPAddress ip;
            if (!_toDiscoveryQueue.TryDequeue(out ip)) {
                OnDiscoverFinished(new DiscoverFinishedEventArgs());
                return;
            }
            ThreadPool.QueueUserWorkItem(_ => DiscoveryIp(ip));
            //DiscoveryIp(ip);
        }

        private void DiscoveryIp(IPAddress ip) {
            if (DiscovererUtil.Discovery(ip, _version)) {
                var arg = new FoundSyncItemEventArgs(new SyncItem(ip, _version));
                OnFoundSyncItem(arg);

                var interfaceList = DiscovererUtil.GetInterfaceIps(ip);
                var neighbors = DiscovererUtil.GetNeighborIps(ip);

                foreach (var i in neighbors) {
                    if (_excludeIPList.Contains(i)) {
                        continue;
                    }
                    _toDiscoveryQueue.Enqueue(i);
                }
                Monitor.Enter(excludeLocker);
                _excludeIPList = _excludeIPList
                                    .Concat(interfaceList)
                                    .Concat(neighbors)
                                    .Distinct().ToList();
                Monitor.Exit(excludeLocker);
            }
            DiscoveryOther();
        }

        private void Discovery(IPAddress ip) {
            String community = SyncEngineConfig.Default.Community;
            Int32 port = SyncEngineConfig.Default.Port;
            Int32 timeout = SyncEngineConfig.Default.Timeout;
            _discoverer.Discover(
                _version,
                new IPEndPoint(ip, port),
                new OctetString(community),
                timeout);
        }

        public void Run() {
            Discovery(IPAddress.Broadcast);
            //ThreadPool.QueueUserWorkItem(_ => { 
            //    Thread.Sleep(5000);
            //    OnDiscoverFinished(null);
            //});           
        }

        private void OnFoundSyncItem(FoundSyncItemEventArgs e) {
            var handle = Interlocked.CompareExchange(ref FoundSyncItem, null, null);
            if (handle != null) {
                handle(this, e);
            }
        }

        private void OnDiscoverFinished(DiscoverFinishedEventArgs e) {
            var handle = Interlocked.CompareExchange(ref DiscoverFinished, null, null);
            if (handle != null) {
                handle(this, e);
            }
        }
    }
}
